package xtime

import (
	"database/sql/driver"
	"errors"
	"fmt"
	"time"
)

const (
	DateFormatTimestamp = "2006-01-02 15:04:05.000000"
	DateFormatLine      = "2006/01/02 15:04:05"
	DateYmdFormatLine   = "2006/01/02"
	DateFormatLineV2    = "2006-01-02 15:04:05"
	DateFormatLineV3    = "2006-01-02"
	DateYmdNoLine       = "20060102"
	DateymdNoLine       = "060102"
	DateYmdNoLineV2     = "20060102150405"
	DateYmdNoLineV3     = "200601"
)

type Time time.Time

func (t Time) Ptr() *Time {
	return &t
}

// Scan 实现 sql.Scanner 接口，处理从数据库里拿出的值
func (t *Time) Scan(value interface{}) error {
	switch vt := value.(type) {
	case string:
		tTime, err := time.ParseInLocation(DateFormatLineV2, vt, time.Local)
		if err != nil {
			return err
		}
		*t = Time(tTime)
	case time.Time:
		*t = Time(vt)
		return nil
	}

	return fmt.Errorf("scan value type err")
}

// Value 实现 driver.Valuer 接口，处理将要写入数据库的值
func (t Time) Value() (driver.Value, error) {
	if t.Time().IsZero() {
		return nil, nil
	}
	return time.Time(t), nil
}

// Time get time.
func (t Time) Time() time.Time {
	return time.Time(t)
}

func (t *Time) UnmarshalJSON(data []byte) error {
	if string(data) == "null" || string(data) == `""` {
		return nil
	}
	// TODO(https://go.dev/issue/47353): Properly unescape a JSON string.
	if len(data) < 2 || data[0] != '"' || data[len(data)-1] != '"' {
		return errors.New("Time.UnmarshalJSON: input is not a JSON string")
	}

	var now time.Time
	var err error
	if now, err = time.ParseInLocation(`"`+DateFormatLineV2+`"`, string(data), time.Local); err == nil {
		*t = Time(now)
		return nil
	}

	if now, err = time.ParseInLocation(`"`+DateFormatLineV3+`"`, string(data), time.Local); err == nil {
		*t = Time(now)
		return nil
	}

	if now, err = time.ParseInLocation(`"`+DateFormatLine+`"`, string(data), time.Local); err == nil {
		*t = Time(now)
		return nil
	}

	if now, err = time.ParseInLocation(`"`+time.RFC3339+`"`, string(data), time.Local); err == nil {
		*t = Time(now)
		return nil
	}

	if now, err = time.ParseInLocation(`"`+DateFormatTimestamp+`"`, string(data), time.Local); err == nil {
		*t = Time(now)
		return nil
	}

	if now, err = time.ParseInLocation(`"`+DateYmdNoLine+`"`, string(data), time.Local); err == nil {
		*t = Time(now)
		return nil
	}

	if now, err = time.ParseInLocation(`"`+DateymdNoLine+`"`, string(data), time.Local); err == nil {
		*t = Time(now)
		return nil
	}

	if now, err = time.ParseInLocation(`"`+DateymdNoLine+`"`, string(data), time.Local); err == nil {
		*t = Time(now)
		return nil
	}

	if now, err = time.ParseInLocation(`"`+DateYmdNoLineV2+`"`, string(data), time.Local); err == nil {
		*t = Time(now)
		return nil
	}

	if now, err = time.ParseInLocation(`"`+DateYmdNoLineV3+`"`, string(data), time.Local); err == nil {
		*t = Time(now)
		return nil
	}

	return err
}

func (t Time) MarshalJSON() ([]byte, error) {
	if t.Time().IsZero() {
		return []byte{'"', '"'}, nil
	}

	b := make([]byte, 0, len(DateFormatLineV2)+2)
	b = append(b, '"')
	b = time.Time(t).AppendFormat(b, DateFormatLineV2)
	b = append(b, '"')
	return b, nil
}

func (t Time) String() string {
	if t.Time().IsZero() {
		return ""
	}
	return time.Time(t).Format(DateFormatLineV2)
}

func (t Time) GormDataType() string {
	return "datetime"
}
